﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Xml.Serialization;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;

namespace BleTest
{
    public partial class MianForm : Form
    {
        private BluetoothLEAdvertisementWatcher Watcher = null;

        private BluetoothLEDevice CurrentDevice = null;

        private int Character_index = 0;
        public List<GattCharacteristic> CurrentCharacteristicList = new List<GattCharacteristic>();

        private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify;

        Dictionary<ulong, BluetoothLEAdvertisement> adv_datas = new Dictionary<ulong, BluetoothLEAdvertisement>(); // 
        Dictionary<ulong, BluetoothLEAdvertisement> res_datas = new Dictionary<ulong, BluetoothLEAdvertisement>(); // Scan Respones

        int listview_index = 0;
        Dictionary<ulong, int> listview_indexs = new Dictionary<ulong, int>();


        public byte[] HexStringToByteArray(string s)
        {
            s = s.Replace(" ", "");
            byte[] buffer = new byte[s.Length / 2];
            for (int i = 0; i < s.Length; i += 2)
            {
                buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
            }
            return buffer;
        }

        public MianForm()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
        }


        private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
        {
            Int16 rssi = eventArgs.RawSignalStrengthInDBm;
            ulong address = eventArgs.BluetoothAddress;
            String name = eventArgs.Advertisement.LocalName.ToString();
            string addr_str = BitConverter.ToString(BitConverter.GetBytes(address), 0, 6).Replace('-', ':').ToLower();

            if (eventArgs.IsScanResponse == true)
            {
                if (res_datas.ContainsKey(address)) res_datas.Remove(address);
                res_datas.Add(address, eventArgs.Advertisement);
            }
            else
            {
                if (adv_datas.ContainsKey(address)) adv_datas.Remove(address);
                adv_datas.Add(address, eventArgs.Advertisement);
            }

            if (None_Name_checkBox.Checked)
            {
                if (name.Length == 0) { return; }
                if (name.Contains(Name_Filter_textBox.Text) == false) { return; }
            }

            if (listview_indexs.TryGetValue(address, out int i))
            {
                if (BLE_Device_listView.Items.Count == 0) return;
                BLE_Device_listView.Items[i].SubItems[1].Text = rssi.ToString();
                if (name.Length > 0) BLE_Device_listView.Items[i].SubItems[3].Text = name;
                return;
            }

            ListViewItem item = new ListViewItem();
            item.Text = addr_str;
            item.Tag = address;
            item.SubItems.Add(rssi.ToString());
            item.SubItems.Add(eventArgs.IsConnectable?"是":"否");
            item.SubItems.Add(name);
            BLE_Device_listView.Items.Add(item);

            listview_indexs.Add(address, listview_index);
            listview_index++;
        }

        public delegate void Gatt_Tree_Add_Server_delegate(string s);
        public void Gatt_Tree_Add_Server(string s)
        {
            TreeNode newNode2 = new TreeNode("服务:" + s);
            newNode2.Tag = -1;
            Gatt_treeView.Nodes.Add(newNode2);
            Gatt_treeView.Select();
        }

        public delegate void Gatt_Tree_Add_Character_delegate(string s, int tag);
        public void Gatt_Tree_Add_Character(string s, int tag)
        {
            int i = this.Gatt_treeView.Nodes.Count - 1;
            TreeNode selectedNode = null;

            if (i < 0)
                selectedNode = Gatt_treeView.TopNode;
            else selectedNode = this.Gatt_treeView.Nodes[i];

            if (selectedNode == null)
            {
                Console.WriteLine("Nul;l");
                return;
            }
            TreeNode newNode2 = new TreeNode("特性:" + s);
            newNode2.Tag = tag;
            selectedNode.Nodes.Add(newNode2);
            selectedNode.Expand();
        }

        private void Connect_Device(BluetoothLEDevice device)
        {
            device.GetGattServicesAsync().Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    CurrentDevice = device;
                    var services = asyncInfo.GetResults().Services;
                    Console.WriteLine("GattServices size=" + services.Count);
                    foreach (GattDeviceService ser in services)
                    {
                        Console.WriteLine(ser.Uuid.ToString());
                        Gatt_treeView.Invoke(new Gatt_Tree_Add_Server_delegate(Gatt_Tree_Add_Server), ser.Uuid.ToString());
                        FindCharacteristic(ser);
                    }
                    Connect_Btn.BackColor = Color.LightGreen;
                    Connect_Btn.Enabled = false;
                }
            };
        }

        private void FindCharacteristic(GattDeviceService gattDeviceService)
        {
            //this.CurrentService = gattDeviceService;
            gattDeviceService.GetCharacteristicsAsync().Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    var services = asyncInfo.GetResults().Characteristics;
                    foreach (var c in services)
                    {
                        Console.WriteLine(c.Uuid.ToString() + "," + c.GetAllDescriptors().ToString() + c.CharacteristicProperties.ToString());

                        Gatt_treeView.Invoke(new Gatt_Tree_Add_Character_delegate(Gatt_Tree_Add_Character), new object[] { c.Uuid.ToString() + " (" + c.CharacteristicProperties.ToString() + ")", Character_index });
                        CurrentCharacteristicList.Add(c);
                        Character_index++;
                    }
                }
            };
        }

        private void write(byte[] data )
        {
            int index = (int)Gatt_treeView.SelectedNode.Tag;
            Console.WriteLine("Select:" + Gatt_treeView.SelectedNode.Tag.ToString());

            if (index == -1)
            {
                Log_textBox.AppendText("\r\n你选择的是服务，请选择一个可以Write的特性~");
                return;
            }
            if ((CurrentCharacteristicList[index].CharacteristicProperties & (GattCharacteristicProperties.Write | GattCharacteristicProperties.WriteWithoutResponse)) == 0)
            {
                Log_textBox.AppendText("\r\n你选择的特性不可写，请选择一个可以Write的特性~");
                return;
            }
            CurrentCharacteristicList[index].WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse);
            string str = CurrentCharacteristicList[index].Uuid.ToString() + " Tx:\r\n" + BitConverter.ToString(data) + "\r\n";
            Log_textBox.AppendText(str);
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            Watcher = new BluetoothLEAdvertisementWatcher
            {
                ScanningMode = BluetoothLEScanningMode.Active
            };
            Watcher.SignalStrengthFilter.InRangeThresholdInDBm = -100;
            Watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -100;
            Watcher.Received += OnAdvertisementReceived;

            // wait 5 seconds to make sure the device is really out of range
            Watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
            Watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);
        }

        private void BLE_Scan_Start_Btn_Click(object sender, EventArgs e)
        {
            listview_indexs.Clear();
            listview_index = 0;
            adv_datas.Clear();
            res_datas.Clear();
            BLE_Device_listView.Items.Clear();
            Watcher.Start();
            BLE_Scan_Start_Btn.Enabled = false;
        }

        private void BLE_Scan_Stop_Btn_Click(object sender, EventArgs e)
        {
            BLE_Scan_Start_Btn.Enabled = true;
            Watcher.Stop();
        }

        private void Connect_Btn_Click(object sender, EventArgs e)
        {
            Watcher.Stop();

            if (BLE_Device_listView.SelectedItems.Count == 0)
            {
                Log_textBox.AppendText("请先选着一个设备！！！\r\n");
                return;
            }
            ulong address = (ulong)BLE_Device_listView.SelectedItems[0].Tag;

            CurrentCharacteristicList.Clear();
            Character_index = 0;
            Gatt_treeView.Nodes.Clear();

            BluetoothLEDevice.FromBluetoothAddressAsync(address).Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    if (asyncInfo.GetResults() == null) { Log_textBox.AppendText("连接设备失败！！！\r\n"); }
                    else { Connect_Device(asyncInfo.GetResults()); }
                }
                else
                {
                    Log_textBox.AppendText("连接设备失败！！！\r\n");
                }
            };
        }

        private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            byte[] data;
            CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
            string str = BitConverter.ToString(data).Replace('-', ' ');
            Console.WriteLine(str);

            Log_textBox.AppendText(sender.Uuid.ToString() + " Rx: \r\n");
            Log_textBox.AppendText(str + "\r\n");
        }


        private void Notify_Btn_Click(object sender, EventArgs e)
        {
            int index = (int)Gatt_treeView.SelectedNode.Tag;
            Console.WriteLine("Select:" + Gatt_treeView.SelectedNode.Tag.ToString());
            CurrentCharacteristicList[index].ValueChanged += Characteristic_ValueChanged;
            CurrentCharacteristicList[index].WriteClientCharacteristicConfigurationDescriptorAsync(CHARACTERISTIC_NOTIFICATION_TYPE).Completed = async (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    GattCommunicationStatus status = asyncInfo.GetResults();
                    if (status == GattCommunicationStatus.Unreachable)
                    {
                        Console.WriteLine("设备不可用");
                    }
                }
            };
        }

        private void Gatt_treeView_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            if ((e.State & TreeNodeStates.Selected) != 0)
            {
                Rectangle rect = e.Bounds;
                rect.Width = rect.Width + 10;
                e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(0, 122, 204)), rect);
                Font nodeFont = e.Node.NodeFont;
                if (nodeFont == null) nodeFont = ((TreeView)sender).Font;
                e.Graphics.DrawString(e.Node.Text, nodeFont, Brushes.White, Rectangle.Inflate(rect, 2, 0));
            }
            else
            {
                e.DrawDefault = true;
            }
        }

        private void Send_Btn_Click(object sender, EventArgs e)
        {
            byte[] data = null;

            if (HEX_checkBox.Checked)
            {
                data = HexStringToByteArray(Send_textBox.Text);
            }
            else
            {
                data = System.Text.Encoding.UTF8.GetBytes(Send_textBox.Text);
            }
            write(data);
        }

        private void Clear_Log_Btn_Click(object sender, EventArgs e)
        {
            Log_textBox.Clear();
        }

        private void RSSI_trackBar_Scroll(object sender, EventArgs e)
        {
            int rssi_threshold = -140 - RSSI_trackBar.Value;
            RSSI_Lable.Text = rssi_threshold.ToString();
        }

        private void BLE_Device_listView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (BLE_Device_listView.SelectedItems.Count == 0) return;
            ulong address = (ulong)BLE_Device_listView.SelectedItems[0].Tag;
            string addr_str = BitConverter.ToString(BitConverter.GetBytes(address), 0, 6).Replace('-', ':').ToLower();
            Log_textBox.AppendText("\r\n设备(" + addr_str + ")的原始广播数据：\r\n");
            Log_textBox.AppendText("广播数据:");
            if (adv_datas.TryGetValue(address, out var adv_data))
            {
                foreach (BluetoothLEAdvertisementDataSection ds in adv_data.DataSections)
                {
                    CryptographicBuffer.CopyToByteArray(ds.Data, out var context_buff);
                    string context = BitConverter.ToString(context_buff).Replace("-", " ").ToUpper() + " ";
                    Log_textBox.AppendText((context_buff.Length).ToString("X2") + " " + ds.DataType.ToString("X2") + " " + context);
                }
            }
            Log_textBox.AppendText("\r\n扫描响应:");
            if (res_datas.TryGetValue(address, out var res_data))
            {
                foreach (BluetoothLEAdvertisementDataSection ds in res_data.DataSections)
                {
                    CryptographicBuffer.CopyToByteArray(ds.Data, out var context_buff);
                    string context = BitConverter.ToString(context_buff).Replace("-", " ").ToUpper() + " ";
                    Log_textBox.AppendText((context_buff.Length).ToString("X2") + " " + ds.DataType.ToString("X2") + " " + context);
                }
            }
            Log_textBox.AppendText("\r\n");
        }

        private void RSSI_trackBar_MouseUp(object sender, MouseEventArgs e)
        {
            if (BLE_Scan_Start_Btn.Enabled) return;
            int rssi_threshold = -140 - RSSI_trackBar.Value;
            Watcher.Stop();
            while (Watcher.Status != BluetoothLEAdvertisementWatcherStatus.Stopped) { Thread.Sleep(100); }
            Thread.Sleep(100);
            Watcher.SignalStrengthFilter.InRangeThresholdInDBm = (short)rssi_threshold;
            Watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = (short)(rssi_threshold - 10);
            BLE_Scan_Start_Btn_Click(null, null);
        }

        private void None_Name_checkBox_CheckedChanged(object sender, EventArgs e)
        {
            if (BLE_Scan_Start_Btn.Enabled) return;
            Watcher.Stop();
            while (Watcher.Status != BluetoothLEAdvertisementWatcherStatus.Stopped) { Thread.Sleep(100); }
            Thread.Sleep(100);
            BLE_Scan_Start_Btn_Click(null, null);
        }

        private void Name_Filter_textBox_TextChanged(object sender, EventArgs e)
        {
            if (None_Name_checkBox.Checked == false) return;
            None_Name_checkBox_CheckedChanged(null, null);
        }

        private void Disconnect_Btn_Click(object sender, EventArgs e)
        {
            if (CurrentDevice == null) return;
            CurrentDevice.Dispose();
            Connect_Btn.Enabled = true;
        }
    }
}
